﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESpace;

namespace CalcCalorie
{
    class Gl
    {
        public const int LastDishesLen = 50;
        public const int LastDishesShowLen = 10;

        public static readonly int ActVerDb = 4;
        public static readonly int ActVerOneDish = 4;

        public static string fPathDB;

        /* public static Dish ImportDishTypeList(String fPathCs)
         {
             Dish dsh = new Dish("ImportedDish");

             String[] sl = File.ReadAllLines(fPathCs, Encoding.Default);
             int actT = -1;
             for (int i = 0; i < sl.Length; i++)
             {
                 string str = sl[i];
                 if (str.Length == 0) continue;

                 if (str[0] == ';' || i == 0)
                 {
                     if (sl[i + 1][0] == ';')
                     {
                         continue;
                     }

                     if (i == 0 && str[0] != ';')
                     {
                         dsh.dl.Add(new Dish(str.Split(';')[0]));
                         i += 1;
                     }
                     else
                     {
                         dsh.dl.Add(new Dish(sl[i + 1].Split(';')[0]));
                         i += 2;
                     }
                     actT++;
                     continue;
                 }

                 string[] s = str.Split(';');
                 dsh.dl[actT].dl.Add(new Dish(s[0], Convert.ToDouble(s[1]), Convert.ToDouble(s[2]), Convert.ToDouble(s[3]), Convert.ToDouble(s[4])));
             }
             return dsh;
         }*/

        public static void ExportDishTypeList(String fPathCs, Dish dsh)
        {
            List<String> sl = new List<string>();
            for (int i = 0; i < dsh.dl.Count; i++)
            {
                sl.Add(dsh.dl[i].Name + ";;;;");
                sl.Add("продукты;калории;белки;жиры;углеводы");
                for (int j = 0; j < dsh.dl[i].dl.Count; j++)
                {
                    sl.Add(dsh.dl[i].dl[j].Name + ";" + dsh.dl[i].dl[j].Cc.ToString() + ";" + dsh.dl[i].dl[j].B.ToString() + ";" + dsh.dl[i].dl[j].G.ToString() + ";" + dsh.dl[i].dl[j].U.ToString());
                }
                if (i < (dsh.dl.Count - 1)) sl.Add(";;;;");
            }
            File.WriteAllLines(fPathCs, sl.ToArray(), Encoding.Default);
        }

        /// <summary>Рекурсивно проверяет названия всех блюд в составном блюде на предмет совпадения</summary>
        /// <param name="Dsh">блюдо (составное или простое)</param>
        /// <param name="CompareName"></param>
        /// <param name="nesting">nesting глубина вложенности. если 0, то сравнивается только с именем блюда. Если 1, то с самым внешним списком. 
        /// При этом с именем основного блюда уже сравнение не идет. То есть, сравнивается только тот уровень вложенности, который указан</param>
        /// <returns></returns>
        public static bool DishNameIsExist(Dish Dsh, String CompareName, int nesting)
        {
            if (nesting < 0) throw new Exception("глубина вложенности должна быть неотрицательным числом");
            if (nesting == 0) return Dsh.Name == CompareName;
            for (int i = 0; i < Dsh.dl.Count; i++)
                if (DishNameIsExist(Dsh.dl[i], CompareName, nesting - 1))
                    return true;

            return false;
        }

        public static string CheckMass(string val)
        {
            int tmp;
            if (!int.TryParse(val.Trim(), out tmp)) return "Масса должна быть целым положительным числом";
            if (tmp < 0) return "Масса должна быть целым положительным числом";
            return "";
        }

        public static DIR DB;
        public static DIR LoadDB(string fPath)
        {
            if (!File.Exists(fPath)) return new DIR("", null);
            EStream st = new EStream(fPath);
            st.OpenLoad();

            int LoadVer = st.LoadInt();
            switch (LoadVer)
            {
                case 4: LoadDB4(st); break;
                default: throw new Exception("Невозможно загрузить базу данных, так как версия не поддерживается");
            }

            st.Close();
            return DB;
        }

        public static void LoadDB4(EStream st)
        {
            DB = new DIR(st, null);
        }

        public static void SaveDB(DIR dir, string fPath)
        {
            EStream st = new EStream(fPath);
            st.OpenSave();

            st.SaveInt(ActVerDb);
            dir.Save(st);

            st.Close();
        }

        static Dish retDish;
        /// <summary>Загрузить отдельное блюдо из файла</summary>
        public static Dish LoadDish(string fPath)
        {
            if (!File.Exists(fPath)) return new Dish("", null);
            EStream st = new EStream(fPath);
            st.OpenLoad();

            int LoadVer = st.LoadInt();
            switch (LoadVer)
            {
                case 4: LoadDish4(st); break;
                default: throw new Exception("Невозможно загрузить отдельное блюдо, так как версия не поддерживается");
            }
           
            st.Close();
            return retDish;
        }

        public static void LoadDish4(EStream st)
        {
            retDish = new Dish(st, null);
        }

        /// <summary>Сохранить отдельное блюдо как файл</summary>
        public static void SaveDish(Dish d, string fPath)
        {
            EStream st = new EStream(fPath);
            st.OpenSave();

            st.SaveInt(ActVerOneDish);
            d.Save(st);

            st.Close();
        }

        /// <summary> Кнвертирование старого блюда в новое </summary>
        public static Dish ConvertOldDish(DishOLD dOld, DBItem Parent)
        {
            Dish ret;

            if (dOld.IsCompl)
            {
                ret = new Dish(dOld.Name, Parent);
                List<DBItem> dl = new List<DBItem>();
                for (int i = 0; i < dOld.dl.Count; i++)
                    dl.Add(ConvertOldDish(dOld.dl[i], ret));
                ret.dl = dl;
                ret.Mass = dOld.Mass;
            }
            else
            {
                ret = new Dish(dOld.Name, Parent, dOld.Cc100, dOld.B100, dOld.G100, dOld.U100, dOld.Mass);
            }

            return ret;
        }

        /// <summary> Кнвертирование старого сложного блюда в каталог в котором содержатся блюда </summary>
        static DIR ConvertOldDishToDIR(DishOLD dOld, DBItem Parent)
        {
            if (!dOld.IsCompl) throw new Exception("Попытка конвертации простого блюда в каталог");
            DIR ret = new DIR(dOld.Name, Parent);
            List<DBItem> dl = new List<DBItem>();
            for (int i = 0; i < dOld.dl.Count; i++)
            {
                dl.Add(ConvertOldDish(dOld.dl[i], ret));
            }
            ret.Items = dl;

            return ret;
        }

        public static DIR ConvertOldDb(DishOLD oldDb)
        {
            DIR ret = new DIR("", null);
            List<DBItem> dl = new List<DBItem>();
            for (int i = 0; i < oldDb.dl.Count; i++)
            {
                dl.Add(ConvertOldDishToDIR(oldDb.dl[i], ret));
            }
            ret.Items = dl;

            return ret;
        }

        public static DBItem LoadRec(EStream st, string ext, DBItem Parent)
        {
            switch (ext.ToUpper())
            {
                case "": return new DIR(st, Parent);
                case "DISH": return new Dish(st, Parent);
            }
            return null;
        }

    }

    // простой каталог с нулевым расширением
    public class DIR : DBItem
    {
        int ActVer = 0;

        public override string Ext { get { return ""; } }

        public DIR(string Name, DBItem Parent)
            : base(Name, true, Parent)
        {
            ;
        }

        public DIR(EStream st, DBItem Parent)
            : base(st, Parent)
        {
            int LoadVer = st.LoadInt(); // задел на версионность 
            switch (LoadVer)
            {
                case 0: Load0(st); break;
                default: throw new Exception("Невозможно загрузить каталог, так как версия не поддерживается");
            }
        }

        public void Load0(EStream st)
        {
            ;
        }
        
        public override void Save(EStream st)
        {
            base.Save(st);
            st.SaveInt(ActVer);
        }

        /*public List<Dish> GetDishList()
        {
            DishCollection dl = GetRecords(new string[1] { "dish" });
            return dl;
        }*/

    }

    public class DishCollection
    {
        List<DBItem> dl;
        public Dish this[int i]
        {
            get { return (Dish)dl[i]; }
            set { dl[i] = value; }
        }

        public DishCollection(List<DBItem> dl) { this.dl = dl; }

        public int Count { get { return dl.Count; } }

        public void Add(DBItem Item) { dl.Add(Item); }

        public void RemoveAt(int ind) { dl.RemoveAt(ind); }

        public void Insert(int ind, DBItem itm) { dl.Insert(ind, itm); }

        public void Clear() { dl.Clear(); }

        // прискаивание некоему списку, объекта (список = dl)
        public static implicit operator List<DBItem>(DishCollection d)
        {
            return d.dl;
        }

        //  присваивание обекту нового списка (dl = список)
        public static implicit operator DishCollection(List<DBItem> d)
        {
            return new DishCollection(d);
        }

        // прискаивание некоему списку, объекта (список = dl)
        /*public static implicit operator List<Dish>(DishCollection d)
        {
            List<Dish> ret = new List<Dish>();
            for (
                int i = 0; i < d.dl.Count; i++)
                ret.Add((Dish)d.dl[i]);
                return ret;
        }

        //  присваивание обекту нового списка (dl = список)
        public static implicit operator DishCollection(List<Dish> dl)
        {
            List<DBItem> itm = new List<DBItem>();
            for (int i = 0; i < dl.Count; i++)
                itm.Add((Dish)dl[i]);
            return new DishCollection(itm);
        }*/

    }

    public class Dish : DBItem
    {
        public override string Ext { get { return "dish"; } }

        double b100 = -1;
        double g100 = -1;
        double u100 = -1;
        double cc100 = 0;

        /// <summary>Масса блюда в граммах</summary> // это не масса входящих в составное блюдо блюд. Это масса, указанная вручную. по умолчанию 100 
        public int Mass = 100; // масса блюда

        public DishCollection dl;

        public List<DBItem> CloneDishList()
        {
            List<DBItem> dList = new List<DBItem>();
            for (int i = 0; i < dl.Count; i++)
                dList.Add(dl[i].Clone());
            return dList;
        }

        public bool IsCompl { get { return dl.Count > 0; } }

        public double B
        {
            get
            {
                if (B100 == -1) return -1;
                return B100 / 100.0 * Mass;
            }
            set
            {
                B100 = (value * 100.0) / Mass;
            }
        }
        public double G
        {
            get
            {
                if (G100 == -1) return -1;
                return G100 / 100.0 * Mass;
            }
            set
            {
                G100 = (value * 100.0) / Mass;
            }
        }
        public double U
        {
            get
            {
                if (U100 == -1) return -1;
                return U100 / 100.0 * Mass;
            }
            set
            {
                U100 = (value * 100.0) / Mass;
            }
        }
        public double Cc
        {
            get
            {
                return Cc100 / 100.0 * Mass;
            }
            set
            {
                Cc100 = (value * 100.0) / Mass;
            }
        }

        public double B100
        {
            get
            {
                if (IsCompl)
                {
                    b100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        if (dl[i].B100 == -1)
                        {
                            b100 = -1;
                            return -1;
                        }
                        allMass += dl[i].Mass;
                        b100 += dl[i].B;
                    }
                    b100 = b100 * 100.0 / allMass;
                }
                return b100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                b100 = value;
            }
        }

        public double G100
        {
            get
            {
                if (IsCompl)
                {
                    g100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        if (dl[i].G100 == -1)
                        {
                            g100 = -1;
                            return -1;
                        }
                        allMass += dl[i].Mass;
                        g100 += dl[i].G;
                    }
                    g100 = g100 * 100.0 / allMass;
                }
                return g100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                g100 = value;
            }
        }

        public double U100
        {
            get
            {
                if (IsCompl)
                {
                    u100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        if (dl[i].U100 == -1)
                        {
                            u100 = -1;
                            return -1;
                        }
                        allMass += dl[i].Mass;
                        u100 += dl[i].U;
                    }
                    u100 = u100 * 100.0 / allMass;
                }
                return u100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                u100 = value;
            }
        }

        public double Cc100
        {
            get
            {
                if (IsCompl)
                {
                    cc100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        cc100 += dl[i].Cc;
                        allMass += dl[i].Mass;
                    }
                    cc100 = cc100 * 100.0 / allMass;
                }
                return cc100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                cc100 = value;
            }
        }

        public String[] DishList
        {
            get
            {
                String[] ret = new String[dl.Count];
                for (int i = 0; i < dl.Count; i++)
                    ret[i] = dl[i].Name;
                return ret;
            }
        }

        public Dish(String Name, DBItem Parent)
            : base(Name, true, Parent)
        {
            dl = new DishCollection(Items);
        }

        public Dish(EStream st, DBItem Parent)
            : base(st, Parent)
        {
            IsDirectory = true;
            dl = new DishCollection(Items);

            int LoadVer = st.LoadInt();
            switch (LoadVer)
            {
                case 4: Load4(st); break;
                default: throw new Exception("Невозможно загрузить блюдо, так как версия не поддерживается"); 
            }
        }

        public Dish(String Name, DBItem Parent, List<DBItem> dishList, int Mass)
            : base(Name, true, Parent)
        {
            Items = dishList;
            dl = new DishCollection(Items);
            this.Mass = Mass;
        }

        public Dish(String Name, DBItem Parent, double Cc100, double B100, double G100, double U100)
            : base(Name, true, Parent)
        {
            dl = new DishCollection(Items);
            Mass = 100;
            b100 = B100;
            g100 = G100;
            u100 = U100;
            cc100 = Cc100;
        }

        public Dish(String Name, DBItem Parent, double Cc100, double B100, double G100, double U100, int Mass)
            : base(Name, true, Parent)
        {
            dl = new DishCollection(Items);
            b100 = B100;
            g100 = G100;
            u100 = U100;
            cc100 = Cc100;
            this.Mass = Mass;
        }

        public Dish Clone()
        {
            if (IsCompl)
            {
                return new Dish(Name, Parent, CloneDishList(), Mass);
            }
            else
            {
                return new Dish(Name, Parent, cc100, b100, g100, u100, Mass);
            }
        }

        public Dish Clone(int newMass)
        {
            if (IsCompl)
            {
                return new Dish(Name, Parent, CloneDishList(), newMass);
            }
            else
            {
                return new Dish(Name, Parent, cc100, b100, g100, u100, newMass);
            }
        }

        public int CompareTo(Dish d)
        {
            return Name.CompareTo(d.Name);
        }

        public override String ToString()
        {
            return "ккал=" + Math.Round(Cc, 1).ToString() +
                "    Б=" + ((B == -1) ? "?" : Math.Round(B, 1).ToString()) +
                "    Ж=" + ((G == -1) ? "?" : Math.Round(G, 1).ToString()) +
                "    У=" + ((U == -1) ? "?" : Math.Round(U, 1).ToString());
        }

        /// <summary>
        /// nesting - на уровене какой вложености считать массы и калорийность блюд
        /// </summary>
        public String ToString(int nesting)
        {
            if (nesting < 1 || !IsCompl) return ToString();

            double tB = GetTotalB(nesting);
            double tG = GetTotalG(nesting);
            double tU = GetTotalU(nesting);
            return "ккал=" + Math.Round(GetTotalCc(nesting), 1).ToString() +
                    "    Б=" + ((tB == -1) ? "?" : Math.Round(tB, 1).ToString()) +
                    "    Ж=" + ((tG == -1) ? "?" : Math.Round(tG, 1).ToString()) +
                    "    У=" + ((tU == -1) ? "?" : Math.Round(tU, 1).ToString());
        }

        /// <summary>Общая масса входящих блюд. считается масса только указанного уровня вложенности</summary>
        public int GetTotalMass(int nesting)
        {
            //if (nesting < 1 || !IsCompl) return mass;
            if (nesting < 1) return Mass;
            int ret = 0;
            for (int i = 0; i < dl.Count; i++)
                ret += dl[i].GetTotalMass(nesting - 1);
            return ret;
        }

        public double GetTotalCc(int nesting)
        {
            if (nesting < 1) return Cc;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
                ret += dl[i].GetTotalCc(nesting - 1);
            return ret;
        }

        public double GetTotalB(int nesting)
        {
            if (nesting < 1) return B;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
            {
                double n = dl[i].GetTotalB(nesting - 1);
                if (n == -1) return -1;
                ret += n;
            }
            return ret;
        }

        public double GetTotalG(int nesting)
        {
            if (nesting < 1) return G;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
            {
                double n = dl[i].GetTotalG(nesting - 1);
                if (n == -1) return -1;
                ret += n;
            }
            return ret;
        }

        public double GetTotalU(int nesting)
        {
            if (nesting < 1) return U;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
            {
                double n = dl[i].GetTotalU(nesting - 1);
                if (n == -1) return -1;
                ret += n;
            }
            return ret;
        }

        #region *** Save\Load ****************************************************

        int ActVer = 4;

        public override void Save(EStream st)
        {
            base.Save(st);

            st.SaveInt(ActVer);

            // сохраняем массу
            st.SaveInt(Mass);

            // сохраняем комплексное или нет
            if (IsCompl) st.SaveByte(1);
            else st.SaveByte(0);

            if (IsCompl)
            {
                st.SaveInt(dl.Count);
                for (int i = 0; i < dl.Count; i++)
                {
                    dl[i].Save(st);
                }
            }
            else
            {
                st.SaveDouble(Cc100);
                st.SaveDouble(B100);
                st.SaveDouble(G100);
                st.SaveDouble(U100);
            }
        }

        public void Load4(EStream st)
        {
            Mass = st.LoadInt();

            bool isCompl = st.LoadByte() == 1;
            if (isCompl)
            {
                int kolvo = st.LoadInt();
                for (int i = 0; i < kolvo; i++)
                {
                    dl.Add(new Dish(st, this));
                }
            }
            else
            {
                Cc100 = st.LoadDouble();
                B100 = st.LoadDouble();
                G100 = st.LoadDouble();
                U100 = st.LoadDouble();
            }
        }

        #endregion *** Save\Load ****************************************************

        public void FillTree(System.Windows.Forms.TreeView tr)
        {
            tr.Nodes.Clear();
            for (int i = 0; i < dl.Count; i++)
            {
                if (dl[i].IsCompl)
                    tr.Nodes.Add(GetTreeNode(dl[i]));
                else
                    tr.Nodes.Add(dl[i].Name);
            }
        }

        System.Windows.Forms.TreeNode GetTreeNode(Dish complDish)
        {
            System.Windows.Forms.TreeNode trn = new System.Windows.Forms.TreeNode();
            trn.Text = complDish.Name;
            for (int i = 0; i < complDish.dl.Count; i++)
            {
                if (complDish.dl[i].IsCompl)
                    trn.Nodes.Add(GetTreeNode(complDish.dl[i]));
                else
                    trn.Nodes.Add(complDish.dl[i].Name);
            }
            return trn;
        }

    }

    public class DishOLD : IComparable<DishOLD>
    {
        public String Name = "";
        double b100 = -1;
        double g100 = -1;
        double u100 = -1;
        double cc100 = 0;

        /// <summary>Масса блюда в граммах</summary> // это не масса входящих в составное блюдо блюд. Это масса, указанная вручную. по умолчанию 100 
        public int Mass = 100; // масса блюда

        public List<DishOLD> dl = new List<DishOLD>();

        public List<DishOLD> CloneDishList()
        {
            List<DishOLD> dList = new List<DishOLD>();
            for (int i = 0; i < dl.Count; i++)
                dList.Add(dl[i].Clone());
            return dList;
        }

        public bool IsCompl { get { return dl.Count > 0; } }

        public double B
        {
            get
            {
                if (B100 == -1) return -1;
                return B100 / 100.0 * Mass;
            }
            set
            {
                B100 = (value * 100.0) / Mass;
            }
        }
        public double G
        {
            get
            {
                if (G100 == -1) return -1;
                return G100 / 100.0 * Mass;
            }
            set
            {
                G100 = (value * 100.0) / Mass;
            }
        }
        public double U
        {
            get
            {
                if (U100 == -1) return -1;
                return U100 / 100.0 * Mass;
            }
            set
            {
                U100 = (value * 100.0) / Mass;
            }
        }
        public double Cc
        {
            get
            {
                return Cc100 / 100.0 * Mass;
            }
            set
            {
                Cc100 = (value * 100.0) / Mass;
            }
        }

        public double B100
        {
            get
            {
                if (IsCompl)
                {
                    b100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        if (dl[i].B100 == -1)
                        {
                            b100 = -1;
                            return -1;
                        }
                        allMass += dl[i].Mass;
                        b100 += dl[i].B;
                    }
                    b100 = b100 * 100.0 / allMass;
                }
                return b100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                b100 = value;
            }
        }

        public double G100
        {
            get
            {
                if (IsCompl)
                {
                    g100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        if (dl[i].G100 == -1)
                        {
                            g100 = -1;
                            return -1;
                        }
                        allMass += dl[i].Mass;
                        g100 += dl[i].G;
                    }
                    g100 = g100 * 100.0 / allMass;
                }
                return g100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                g100 = value;
            }
        }

        public double U100
        {
            get
            {
                if (IsCompl)
                {
                    u100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        if (dl[i].U100 == -1)
                        {
                            u100 = -1;
                            return -1;
                        }
                        allMass += dl[i].Mass;
                        u100 += dl[i].U;
                    }
                    u100 = u100 * 100.0 / allMass;
                }
                return u100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                u100 = value;
            }
        }

        public double Cc100
        {
            get
            {
                if (IsCompl)
                {
                    cc100 = 0;
                    int allMass = 0;
                    for (int i = 0; i < dl.Count; i++)
                    {
                        cc100 += dl[i].Cc;
                        allMass += dl[i].Mass;
                    }
                    cc100 = cc100 * 100.0 / allMass;
                }
                return cc100;
            }
            set
            {
                if (IsCompl) throw new Exception("Попытка присвоения комплексному блюду");
                cc100 = value;
            }
        }

        public String[] DishList
        {
            get
            {
                String[] ret = new String[dl.Count];
                for (int i = 0; i < dl.Count; i++)
                    ret[i] = dl[i].Name;
                return ret;
            }
        }

        public DishOLD(String Name)
        {
            this.Name = Name;
        }

        public DishOLD(String fPath, string dishName)
        {
            Load(fPath);
        }

        public DishOLD(String Name, List<DishOLD> dishList)
        {
            this.Name = Name;
            dl = dishList;
        }

        public DishOLD(String Name, List<DishOLD> dishList, int Mass)
        {
            this.Name = Name;
            dl = dishList;
            this.Mass = Mass;
        }

        public DishOLD(String Name, double Cc100, double B100, double G100, double U100)
        {
            this.Name = Name;
            Mass = 100;
            b100 = B100;
            g100 = G100;
            u100 = U100;
            cc100 = Cc100;
        }

        public DishOLD(String Name, double Cc100, double B100, double G100, double U100, int Mass)
        {
            this.Name = Name;
            b100 = B100;
            g100 = G100;
            u100 = U100;
            cc100 = Cc100;
            this.Mass = Mass;
        }

        public DishOLD Clone()
        {
            if (IsCompl)
            {
                return new DishOLD(Name, CloneDishList(), Mass);
            }
            else
            {
                return new DishOLD(Name, cc100, b100, g100, u100, Mass);
            }
        }

        public DishOLD Clone(int newMass)
        {
            if (IsCompl)
            {
                return new DishOLD(Name, CloneDishList(), newMass);
            }
            else
            {
                return new DishOLD(Name, cc100, b100, g100, u100, newMass);
            }
        }

        public int CompareTo(DishOLD d)
        {
            return Name.CompareTo(d.Name);
        }

        public override String ToString()
        {
            return "ккал=" + Math.Round(Cc, 1).ToString() +
                "    Б=" + ((B == -1) ? "?" : Math.Round(B, 1).ToString()) +
                "    Ж=" + ((G == -1) ? "?" : Math.Round(G, 1).ToString()) +
                "    У=" + ((U == -1) ? "?" : Math.Round(U, 1).ToString());
        }

        /// <summary>
        /// nesting - на уровене какой вложености считать массы и калорийность блюд
        /// </summary>
        public String ToString(int nesting)
        {
            if (nesting < 1 || !IsCompl) return ToString();

            double tB = GetTotalB(nesting);
            double tG = GetTotalG(nesting);
            double tU = GetTotalU(nesting);
            return "ккал=" + Math.Round(GetTotalCc(nesting), 1).ToString() +
                    "    Б=" + ((tB == -1) ? "?" : Math.Round(tB, 1).ToString()) +
                    "    Ж=" + ((tG == -1) ? "?" : Math.Round(tG, 1).ToString()) +
                    "    У=" + ((tU == -1) ? "?" : Math.Round(tU, 1).ToString());
        }

        /// <summary>Общая масса входящих блюд. считается масса только указанного уровня вложенности</summary>
        public int GetTotalMass(int nesting)
        {
            //if (nesting < 1 || !IsCompl) return mass;
            if (nesting < 1) return Mass;
            int ret = 0;
            for (int i = 0; i < dl.Count; i++)
                ret += dl[i].GetTotalMass(nesting - 1);
            return ret;
        }

        public double GetTotalCc(int nesting)
        {
            if (nesting < 1) return Cc;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
                ret += dl[i].GetTotalCc(nesting - 1);
            return ret;
        }

        public double GetTotalB(int nesting)
        {
            if (nesting < 1) return B;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
            {
                double n = dl[i].GetTotalB(nesting - 1);
                if (n == -1) return -1;
                ret += n;
            }
            return ret;
        }

        public double GetTotalG(int nesting)
        {
            if (nesting < 1) return G;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
            {
                double n = dl[i].GetTotalG(nesting - 1);
                if (n == -1) return -1;
                ret += n;
            }
            return ret;
        }

        public double GetTotalU(int nesting)
        {
            if (nesting < 1) return U;
            double ret = 0;
            for (int i = 0; i < dl.Count; i++)
            {
                double n = dl[i].GetTotalU(nesting - 1);
                if (n == -1) return -1;
                ret += n;
            }
            return ret;
        }

        #region *** Save\Load ****************************************************

        int ActVer = 2;

        public static void SaveObject(String fPath, int ActVer, List<SLObjectFunc> funcList)
        {
            // ищем функцию для необходимой версии
            int ind = -1;
            for (int i = 0; i < funcList.Count; i++)
                if (funcList[i].ver == ActVer)
                {
                    ind = i;
                    break;
                }
            // если фия не найдена, генерируем исключение
            if (ind == -1)
                throw new Exception("Не найдена функция для сохранения в версии " + ActVer.ToString());

            // сохраняем
            EStream st = new EStream(fPath);
            st.OpenSave();
            st.SaveInt(ActVer);
            funcList[ind].func(st);
            st.Close();
        }

        public static int LoadObject(String fPath, List<SLObjectFunc> funcList)
        {
            EStream st = new EStream(fPath);
            st.OpenLoad();
            int ver = st.LoadInt();
            int ind = -1;
            for (int i = 0; i < funcList.Count; i++)
                if (funcList[i].ver == ver)
                {
                    ind = i;
                    break;
                }
            if (ind == -1)
                throw new Exception("Не найдена функция для загрузки в версии " + ver.ToString());

            funcList[ind].func(st);
            st.Close();
            return ver;
        }

        public delegate void SLObjectDelegate(EStream st);

        public class SLObjectFunc
        {
            public SLObjectDelegate func;
            public int ver;
            public SLObjectFunc(SLObjectDelegate Func, int Ver)
            {
                func = Func;
                ver = Ver;
            }
        }

        public void Save(String fPath)
        {
            List<SLObjectFunc> funcL = new List<SLObjectFunc>();
            funcL.Add(new SLObjectFunc(new SLObjectDelegate(Save1), 1));
            funcL.Add(new SLObjectFunc(new SLObjectDelegate(Save2), 2));
            SaveObject(fPath, ActVer, funcL);
        }

        void Load(String fPath)
        {
            if (!File.Exists(fPath)) return;
            List<SLObjectFunc> funcL = new List<SLObjectFunc>();
            funcL.Add(new SLObjectFunc(new SLObjectDelegate(Load0), 0));
            funcL.Add(new SLObjectFunc(new SLObjectDelegate(Load1), 1));
            funcL.Add(new SLObjectFunc(new SLObjectDelegate(Load2), 2));
            LoadObject(fPath, funcL);
        }

        void Save1(EStream st)
        {
            SaveDish(st, this);
        }

        void Save2(EStream st)
        {
            SaveDish1(st, this);
        }

        void Load0(EStream st)
        {
            dl.Clear();
            // количество
            int kolvo = st.LoadInt();
            for (int t = 0; t < kolvo; t++)
            {
                dl.Add(new DishOLD(st.LoadStr()));
                int kolVoD = st.LoadInt();
                for (int i = 0; i < kolVoD; i++)
                {
                    DishOLD newD = new DishOLD("", new List<DishOLD>());
                    LoadDish(st, newD);
                    dl[t].dl.Add(newD);
                }
            }
        }

        void Load1(EStream st)
        {
            LoadDish(st, this);
        }

        void Load2(EStream st)
        {
            LoadDish1(st, this);
        }

        void LoadDish(EStream st, DishOLD d)
        {
            d.Name = st.LoadStr();

            bool isCompl = st.LoadByte() == 1;

            if (isCompl)
            {
                int kolvo = st.LoadInt();
                for (int i = 0; i < kolvo; i++)
                {
                    DishOLD newD = new DishOLD("", new List<DishOLD>());
                    LoadDish(st, newD);
                    d.dl.Add(newD);
                }
            }
            else
            {
                d.Cc100 = st.LoadDouble();
                d.B100 = st.LoadDouble();
                d.G100 = st.LoadDouble();
                d.U100 = st.LoadDouble();
                d.Mass = st.LoadInt();
            }
        }

        void LoadDish1(EStream st, DishOLD d)
        {
            d.Name = st.LoadStr();
            d.Mass = st.LoadInt();

            bool isCompl = st.LoadByte() == 1;

            if (isCompl)
            {
                int kolvo = st.LoadInt();
                for (int i = 0; i < kolvo; i++)
                {
                    DishOLD newD = new DishOLD("", new List<DishOLD>());
                    LoadDish1(st, newD);
                    d.dl.Add(newD);
                }
            }
            else
            {
                d.Cc100 = st.LoadDouble();
                d.B100 = st.LoadDouble();
                d.G100 = st.LoadDouble();
                d.U100 = st.LoadDouble();

            }
        }

        void SaveDish(EStream st, DishOLD d)
        {
            // сохраняем имя
            st.SaveStr(d.Name);

            // сохраняем комплексное или нет
            if (d.IsCompl) st.SaveByte(1);
            else st.SaveByte(0);

            if (d.IsCompl)
            {
                st.SaveInt(d.dl.Count);
                for (int i = 0; i < d.dl.Count; i++)
                {
                    SaveDish(st, d.dl[i]);
                }
            }
            else
            {
                st.SaveDouble(d.Cc100);
                st.SaveDouble(d.B100);
                st.SaveDouble(d.G100);
                st.SaveDouble(d.U100);
                // сохраняем массу
                st.SaveInt(d.Mass);
            }
        }

        void SaveDish1(EStream st, DishOLD d)
        {
            // сохраняем имя
            st.SaveStr(d.Name);

            // сохраняем массу
            st.SaveInt(d.Mass);

            // сохраняем комплексное или нет
            if (d.IsCompl) st.SaveByte(1);
            else st.SaveByte(0);

            if (d.IsCompl)
            {
                st.SaveInt(d.dl.Count);
                for (int i = 0; i < d.dl.Count; i++)
                {
                    SaveDish1(st, d.dl[i]);
                }
            }
            else
            {
                st.SaveDouble(d.Cc100);
                st.SaveDouble(d.B100);
                st.SaveDouble(d.G100);
                st.SaveDouble(d.U100);
            }
        }

        #endregion *** Save\Load ****************************************************

        public void FillTree(System.Windows.Forms.TreeView tr)
        {
            tr.Nodes.Clear();
            for (int i = 0; i < dl.Count; i++)
            {
                if (dl[i].IsCompl)
                    tr.Nodes.Add(GetTreeNode(dl[i]));
                else
                    tr.Nodes.Add(dl[i].Name);
            }
        }

        System.Windows.Forms.TreeNode GetTreeNode(DishOLD complDish)
        {
            System.Windows.Forms.TreeNode trn = new System.Windows.Forms.TreeNode();
            trn.Text = complDish.Name;
            for (int i = 0; i < complDish.dl.Count; i++)
            {
                if (complDish.dl[i].IsCompl)
                    trn.Nodes.Add(GetTreeNode(complDish.dl[i]));
                else
                    trn.Nodes.Add(complDish.dl[i].Name);
            }
            return trn;
        }

    }

}
